#pragma once
#include <cstdint>
#include <Vector3.h>
#include <Quaternion.h>
#include <corecrt_math.h>
#include <Vector2.h>
#include "ListBase.h"
#include "StringBase.h"
#include "animtypes.h"

typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned __int64 uint64;
typedef Math::Quaternion Quaternion;
typedef Math::Vector3 RadianEuler;
typedef uint16_t float16; // it really does not like Math::Half


#define MAX_NUM_LODS				8
#define MAX_NUM_BONES_PER_VERT		3
#define MAX_NUM_EXTRA_BONE_WEIGHTS	16

#define STUDIO_VERSION_TITANFALL	52
#define STUDIO_VERSION_TITANFALL2	53
#define STUDIO_VERSION_APEX_LEGENDS	54

#define MODEL_FILE_ID		(('T'<<24)+('S'<<16)+('D'<<8)+'I')

// This flag is set if no hitbox information was specified
#define STUDIOHDR_FLAGS_AUTOGENERATED_HITBOX	0x1

// NOTE:  This flag is set at loadtime, not mdl build time so that we don't have to rebuild
// models when we change materials.
#define STUDIOHDR_FLAGS_USES_ENV_CUBEMAP		0x2

// Use this when there are translucent parts to the model but we're not going to sort it 
#define STUDIOHDR_FLAGS_FORCE_OPAQUE			0x4

// Use this when we want to render the opaque parts during the opaque pass
// and the translucent parts during the translucent pass
#define STUDIOHDR_FLAGS_TRANSLUCENT_TWOPASS		0x8

// This is set any time the .qc files has $staticprop in it
// Means there's no bones and no transforms
#define STUDIOHDR_FLAGS_STATIC_PROP				0x10

// NOTE:  This flag is set at loadtime, not mdl build time so that we don't have to rebuild
// models when we change materials.
#define STUDIOHDR_FLAGS_USES_FB_TEXTURE		    0x20

// This flag is set by studiomdl.exe if a separate "$shadowlod" entry was present
//  for the .mdl (the shadow lod is the last entry in the lod list if present)
#define STUDIOHDR_FLAGS_HASSHADOWLOD			0x40

// NOTE:  This flag is set at loadtime, not mdl build time so that we don't have to rebuild
// models when we change materials.
#define STUDIOHDR_FLAGS_USES_BUMPMAPPING		0x80

// NOTE:  This flag is set when we should use the actual materials on the shadow LOD
// instead of overriding them with the default one (necessary for translucent shadows)
#define STUDIOHDR_FLAGS_USE_SHADOWLOD_MATERIALS	0x100

// NOTE:  This flag is set when we should use the actual materials on the shadow LOD
// instead of overriding them with the default one (necessary for translucent shadows)
#define STUDIOHDR_FLAGS_OBSOLETE				0x200

#define STUDIOHDR_FLAGS_UNUSED					0x400

// NOTE:  This flag is set at mdl build time
#define STUDIOHDR_FLAGS_NO_FORCED_FADE			0x800

// NOTE:  The npc will lengthen the viseme check to always include two phonemes
#define STUDIOHDR_FLAGS_FORCE_PHONEME_CROSSFADE	0x1000

// This flag is set when the .qc has $constantdirectionallight in it
// If set, we use constantdirectionallightdot to calculate light intensity
// rather than the normal directional dot product
// only valid if STUDIOHDR_FLAGS_STATIC_PROP is also set
#define STUDIOHDR_FLAGS_CONSTANT_DIRECTIONAL_LIGHT_DOT 0x2000

// Flag to mark delta flexes as already converted from disk format to memory format
//STUDIOHDR_FLAGS_COMPLEX_WEIGHTS
#define STUDIOHDR_FLAGS_FLEXES_CONVERTED		0x4000

// Indicates the studiomdl was built in preview mode
#define STUDIOHDR_FLAGS_BUILT_IN_PREVIEW_MODE	0x8000

// Ambient boost (runtime flag)
#define STUDIOHDR_FLAGS_AMBIENT_BOOST			0x10000

// Don't cast shadows from this model (useful on first-person models)
#define STUDIOHDR_FLAGS_DO_NOT_CAST_SHADOWS		0x20000

// alpha textures should cast shadows in vrad on this model (ONLY prop_static!)
#define STUDIOHDR_FLAGS_CAST_TEXTURE_SHADOWS	0x40000

// Model has a quad-only Catmull-Clark SubD cage
#define STUDIOHDR_FLAGS_SUBDIVISION_SURFACE		0x80000

// flagged on load to indicate no animation events on this model
#define STUDIOHDR_FLAGS_NO_ANIM_EVENTS			0x100000

// If flag is set then studiohdr_t.flVertAnimFixedPointScale contains the
// scale value for fixed point vert anim data, if not set then the
// scale value is the default of 1.0 / 4096.0.  Regardless use
// studiohdr_t::VertAnimFixedPointScale() to always retrieve the scale value
#define STUDIOHDR_FLAGS_VERT_ANIM_FIXED_POINT_SCALE	0x200000

// new in respawn models
#define STUDIOHDR_FLAGS_USES_EXTRA_BONE_WEIGHTS		        0x4000 // don't really know what to name this one, new in v54, for 'vvw'
#define STUDIOHDR_FLAGS_USES_VERTEX_COLOR	        0x1000000 // model has/uses vertex color
#define STUDIOHDR_FLAGS_USES_UV2					0x2000000 // model has/uses secondary uv layer

#pragma pack(push, 1)
struct matrix3x4_t
{
	// unsure if that's how it actually works

	// row 1, x
	float c0r0; // x
	float c1r0; // x
	float c2r0; // x
	float c3r0; // x

	// row 2, y
	float c0r1; // y
	float c1r1; // y
	float c2r1; // y
	float c3r1; // y

	// row 3, z
	float c0r2; // z
	float c1r2; // z
	float c2r2; // z
	float c3r2; // z

	Vector3 GetRotationMatrixAsDegrees()
	{
		// c0r0, c1r0, c2r0, c0r1, c1r1, c2r1, c2r2
		float x, y, z, tX, tY;

		float m2 = c2r0;
		if (m2 < -1)
			m2 = -1;
		else if (m2 > 1)
			m2 = 1;

		y = -asin(m2);

		printf("%f\n", m2);

		float cosY = cos(y);

		y = Math::MathHelper::RadiansToDegrees(y);

		if (abs(cosY) > 0.005)
		{
			tX = c2r2 / cosY;
			tY = -c2r1 / cosY;
			x = Math::MathHelper::RadiansToDegrees(atan2(tY, tX));

			tX = c0r0 / cosY;
			tY = -c1r0 / cosY;
			z = Math::MathHelper::RadiansToDegrees(atan2(tY, tX));
		}
		else
		{
			x = 0;
			tX = c1r1 / cosY;
			tY = c0r1 / cosY;
			z = Math::MathHelper::RadiansToDegrees(atan2(tY, tX));
		}


		// these can take some strange orders and idrk how to determine it
		//if (m2 == 0)
		//{
		//	return { x,z,y };
		//}
		//else if (m2 < 1 && m2 != -1)
		//{
		//	return { z,y,x };
		//}

		return { x,y,z };
	}
};

// placeholders, needs to be done better and or like normal source
struct Quaternion64
{
	uint64 x : 21;
	uint64 y : 21;
	uint64 z : 21;
	uint64 wneg : 1;
};

struct Vector48
{
	float16 x;
	float16 y;
	float16 z;
};

struct studiohdr_t // latest studiohdr
{
	int id; // Model format ID, such as "IDST" (0x49 0x44 0x53 0x54)
	int version; // Format version number, such as 48 (0x30,0x00,0x00,0x00)
	int checksum; // This has to be the same in the phy and vtx files to load!
	int sznameindex; // This has been moved from studiohdr2 to the front of the main header.
	char name[64]; // The internal name of the model, padding with null bytes.
	// Typically "my_model.mdl" will have an internal name of "my_model"
	int length; // Data size of MDL file in bytes.

	Vector3 eyeposition;	// ideal eye position

	Vector3 illumposition;	// illumination center

	Vector3 hull_min;		// ideal movement hull size
	Vector3 hull_max;

	Vector3 view_bbmin;		// clipping bounding box
	Vector3 view_bbmax;

	int flags;

	int numbones; // bones
	int boneindex;

	int numbonecontrollers; // bone controllers
	int bonecontrollerindex;

	int numhitboxsets;
	int hitboxsetindex;

	int numlocalanim; // animations/poses
	int localanimindex; // animation descriptions

	int numlocalseq; // sequences
	int	localseqindex;

	int activitylistversion; // initialization flag - have the sequences been indexed?

	// mstudiotexture_t
	// short rpak path
	// raw textures
	int materialtypesindex;
	int numtextures; // the material limit exceeds 128, probably 256.
	int textureindex;

	// this should always only be one, unless using vmts.
	// raw textures search paths
	int numcdtextures;
	int cdtextureindex;

	// replaceable textures tables
	int numskinref;
	int numskinfamilies;
	int skinindex;

	int numbodyparts;
	int bodypartindex;

	int numlocalattachments;
	int localattachmentindex;

	// not unks but I am not filling this out because it changes from 12.1, 12.2, 13, and 14
	uint8_t Unknown2[0x14];

	uint32_t NahhhO;
	uint32_t SubmeshLodsOffset;

	uint32_t Unk;
	uint32_t SubmeshLodsOffset_V14;

	uint8_t Unknown3[0x3C];
	uint32_t OffsetToBoneRemapInfo;
	uint32_t BoneRemapCount;
	uint32_t OffsetToBoneRemapInfo_V14;
	uint32_t BoneRemapCount_V14;
};

struct s3studiohdr_t // season 3 studiohdr
{
	int id; // Model format ID, such as "IDST" (0x49 0x44 0x53 0x54)
	int version; // Format version number, such as 48 (0x30,0x00,0x00,0x00)
	int checksum; // This has to be the same in the phy and vtx files to load!
	int sznameindex; // This has been moved from studiohdr2 to the front of the main header.
	char name[64]; // The internal name of the model, padding with null bytes.
	// Typically "my_model.mdl" will have an internal name of "my_model"
	int length; // Data size of MDL file in bytes.

	Vector3 eyeposition;	// ideal eye position

	Vector3 illumposition;	// illumination center

	Vector3 hull_min;		// ideal movement hull size
	Vector3 hull_max;

	Vector3 view_bbmin;		// clipping bounding box
	Vector3 view_bbmax;

	int flags;

	int numbones; // bones
	int boneindex;

	int numbonecontrollers; // bone controllers
	int bonecontrollerindex;

	int numhitboxsets;
	int hitboxsetindex;

	int numlocalanim; // animations/poses
	int localanimindex; // animation descriptions

	int numlocalseq; // sequences
	int	localseqindex;

	int activitylistversion; // initialization flag - have the sequences been indexed?

	// mstudiotexture_t
	// short rpak path
	// raw textures
	int materialtypesindex;
	int numtextures; // the material limit exceeds 128, probably 256.
	int textureindex;

	// this should always only be one, unless using vmts.
	// raw textures search paths
	int numcdtextures;
	int cdtextureindex;

	// replaceable textures tables
	int numskinref;
	int numskinfamilies;
	int skinindex;

	int numbodyparts;
	int bodypartindex;

	int numlocalattachments;
	int localattachmentindex;

	int numlocalnodes;
	int localnodeindex;
	int localnodenameindex;

	int numunknodes;
	int unknodexindex;

	int meshindex; // SubmeshLodsOffset, might just be a mess offset

	int deprecated_numflexcontrollers;
	int deprecated_flexcontrollerindex;

	int deprecated_numflexrules;
	int deprecated_flexruleindex;

	int numikchains;
	int ikchainindex;

	// this is rui meshes
	int numruimeshes;
	int ruimeshindex;

	int numlocalposeparameters;
	int localposeparamindex;

	int surfacepropindex;

	int keyvalueindex;
	int keyvaluesize;

	int numlocalikautoplaylocks;
	int localikautoplaylockindex;

	float mass;
	int contents;

	// unused for packed models
	int numincludemodels;
	int includemodelindex;

	uint32_t virtualModel;

	int bonetablebynameindex;

	// if STUDIOHDR_FLAGS_CONSTANT_DIRECTIONAL_LIGHT_DOT is set,
	// this value is used to calculate directional components of lighting 
	// on static props
	byte constdirectionallightdot;

	// set during load of mdl data to track *desired* lod configuration (not actual)
	// the *actual* clamped root lod is found in studiohwdata
	// this is stored here as a global store to ensure the staged loading matches the rendering
	byte rootLOD;

	// set in the mdl data to specify that lod configuration should only allow first numAllowRootLODs
	// to be set as root LOD:
	//	numAllowedRootLODs = 0	means no restriction, any lod can be set as root lod.
	//	numAllowedRootLODs = N	means that lod0 - lod(N-1) can be set as root lod, but not lodN or lower.
	byte numAllowedRootLODs;

	byte unused;

	float fadeDistance;

	float gathersize; // what. from r5r struct

	int numunk_v54_early;
	int unkindex_v54_early;

	float flVertAnimFixedPointScale; // to be verified
	int surfacepropLookup; // saved in the file

	int sourceFilenameOffset;

	int numsrcbonetransform;
	int srcbonetransformindex;

	int	illumpositionattachmentindex;

	int linearboneindex;

	int m_nBoneFlexDriverCount; // unsure if that's what it is in apex
	int m_nBoneFlexDriverIndex;

	int unkindexflex;

	int unk1_v54[6];

	// always "" or "Titan"
	int unkstringindex;

	// this is now used for combined files in rpak, vtx, vvd, and vvc are all combined while vphy is separate.
	// the indexes are added to the offset in the rpak mdl_ header.
	// vphy isn't vphy, looks like a heavily modified vphy.
	int vtxindex; // VTX
	int vvdindex; // VVD / IDSV
	int vvcindex; // VVC / IDCV 
	int vphyindex; // VPHY / IVPS

	int vtxsize;
	int vvdsize;
	int vvcsize;
	int vphysize; // still used in models using vg

	// unused in apex
    int unkmemberindex1;
    int numunkmember1;

    // only seen on '_animated' suffixed models so far
    int unkcount3;
    int unkindex3;

	// Per Tri Collision AABB size
	Vector3 mins;
	Vector3 maxs; // seem to be the same as hull size

	int unk3_v54[3];

	int unkindex4; // chunk before unkindex3 sometimes

	short unk4_v54[2]; // same as unk3_v54_v121

	int weightindex;
	int weightsize;
};

struct studiohdr_t_v16
{
	int flags;
	int checksum; // unsure if this is still checksum, there isn't any other files that have it still
	short sznameindex; // No longer stored in string block, uses string in header.
	char name[32]; // The internal name of the model, padding with null bytes.
	// Typically "my_model.mdl" will have an internal name of "my_model"
	byte unk_v16;

	byte surfacepropLookup; // saved in the file

	float mass;

	int unk1_v16;

	uint16 hitboxsetindex;
	byte numhitboxsets;

	byte illumpositionattachmentindex;

	Vector3 illumposition;	// illumination center

	Vector3 hull_min;		// ideal movement hull size
	Vector3 hull_max;

	Vector3 view_bbmin;		// clipping bounding box
	Vector3 view_bbmax;

	short numbones; // bones
	uint16 boneindex;
	uint16 bonedataindex;

	short numlocalseq; // sequences
	uint16 localseqindex;

	byte unkfill[5];

	byte numlocalattachments;
	uint16 localattachmentindex;

	short numlocalnodes;
	uint16 localnodenameindex;
	uint16 localnodeindex;

	short numikchains;
	uint16 ikchainindex;

	short numtextures; // the material limit exceeds 128, probably 256.
	uint16 textureindex;

	// replaceable textures tables
	short numskinref;
	short numskinfamilies;
	uint16 skinindex;

	short numbodyparts;
	uint16 bodypartindex;

	// this is rui meshes
	short numruimeshes;
	uint16 ruimeshindex;

	short numlocalposeparameters;
	uint16 localposeparamindex;

	uint16 surfacepropindex;

	uint16 keyvalueindex;

	uint16 vgmeshindex;
	short numvgmeshes;

	short bonetablebynameindex;

	uint16 boneremapindex;
	short numboneremaps;

	uint16 vgloddataindex;
	short numvgloddata;

	uint16 vglodheaderindex;
	short numvglodheader;

	float fadedistance;

	float gathersize; // what. from r5r struct

	short numsrcbonetransform;
	uint16 srcbonetransformindex;

	// asset bakery strings if it has any
	uint16 mayaindex;

	uint16 linearboneindex;

	short m_nBoneFlexDriverCount; // unsure if that's what it is in apex
	uint16 m_nBoneFlexDriverIndex;
	uint16 unkindexflex;

	short unkcount3; // same as v54
	uint16 unkindex3; // same as v54

	uint16 unkindex4; // same as v54

	byte unk5_v16; // unk4_v54[0]
	byte unk6_v16; // unk4_v54[1]
	short unk7_v16; // unk4_v54[2]
	short unk8_v16;
	short unk9_v16;

	//uint16 unkshorts[7];
};

struct mstudiolinearbone_t_v16
{
	unsigned short numbones;

	unsigned short flagsindex; // int

	unsigned short parentindex; // short

	unsigned short posindex; // vector3

	unsigned short quatindex; // quaternion

	unsigned short rotindex; // radianeuler

	unsigned short posetoboneindex; // matrix3x4_t
};

struct vgloddata_t_v16
{
	int vgoffset; // offset to this section in vg
	int vgsizecompressed;
	int vgsizedecompressed; // decompressed size of data in vg

	byte numMeshes;

	// this could also be vg section index
	byte lodlevel; // 0, 1, 2, 3, etc
	byte numlods; // normally 1, stolen from vg structs

	// ids for something
	byte unk2; // powers of two
};

struct mstudiobone_t
{
	uint32_t NameOffset;		// Relative to current position
	int32_t ParentIndex;

	int BoneControllers[6]; // -1 if none

	Math::Vector3 Position;		// Local
	Math::Quaternion Rotation;	// Local

	uint8_t Padding[0x78];
};

struct mstudiobone_t_v16
{
	int contents;

	byte unk;

	byte surfacepropLookup; // written on compile in v54+
	short surfacepropidx; // index into string tablefor property name
	short physicsbone; // index into physically simulated bone
	short sznameindex;
};

struct mstudiobonedata_t_v16
{
	matrix3x4_t poseToBone;
	Math::Quaternion qAlignment;

	// default values
	Math::Vector3 pos;
	Math::Quaternion quat;
	Math::Vector3 rot;

	Vector3 unkvector; // the same as whatever v53 is

	short parent; // parent bone;

	short unk1;

	int flags;

	byte unkid;

	byte proctype;
	uint16 procindex; // procedural rule
};

struct mstudiomodelv54_t
{
	char name[64];

	int unkindex2; // byte before string block

	// these are probably still used but get written over the name if nothing is set.
	//int type;
	//float boundingradius;

	char name2[8]; // it looks like they write the entire name
	// then write over it with other values where needed
	// why.

	int nummeshes;
	int meshindex;

	// cache purposes
	int numvertices; // number of unique vertices/normals/texcoords
	int vertexindex; // vertex Vector
	int tangentsindex; // tangents Vector

	int numattachments;
	int attachmentindex;

	// might be cut
	int numeyeballs;
	int eyeballindex;

	//mstudio_modelvertexdata_t vertexdata;

	// same as v53, except trimming the fat
	int unk[4];

	int unkindex;
	int unkindex1;
};

struct RMdlVGHeaderOld
{
	uint32_t Magic;		// 0x47567430	'0tvg'
	uint32_t Version;	// 0x1
	uint32_t Unknown;	// Usually 0
	uint32_t DataSize;	// Total size of data + header in starpak

	uint64_t BoneRemapOffset;
	uint64_t BoneRemapCount;		// Only 1 byte each

	uint64_t MeshOffset;
	uint64_t MeshCount;		// 0x48 each

	uint64_t IndexOffset;
	uint64_t IndexCount;		// 0x2 each (uint16_t)

	uint64_t VertexBufferOffset;
	uint64_t VertexBufferSize; // 1 byte each

	uint64_t ExtendedWeightsOffset;
	uint64_t ExtendedWeightsCount;		// Only 1 byte per count

	uint64_t Unknown2Offset;
	uint64_t Unknown2Count;		// 0x30 each

	uint64_t LodOffset;
	uint64_t LodCount;			// 0x8 each

	uint64_t ExternalWeightsOffset;
	uint64_t ExternalWeightsCount;	// 0x10 each

	uint64_t StripsOffset;
	uint64_t StripsCount;			// 0x23 each
};

struct RMdlVGHeader
{
	int id;		// 0x47567430	'0tVG'
	int version;	// 0x1
	int padding;
	uint32_t lodCount;	// If 0x1, this IS the first and only lod, if > 0x1, MORE 0tVG headers follow PER lod count
	uint32_t unk;
	uint32_t unk1;
	uint32_t lodOffset;
	char unk3[8];
};

struct VGHeader_t_v16
{
	short unk0;
	short unk2;
	int unk4;
	int nummeshes;
	int meshindex;
};

struct VGLod
{
	char unk[4];
	uint32_t dataSize;
	short meshCount;
	char unk1; // both of these bytes line up with the LOD index
	char unk2;
	float distance;
	uint64_t meshOffset;
};

struct RMdlVGIndexCountPacked
{
	uint64_t Count : 56;
	uint64_t Type : 8;
};

struct RMdlVGIndexCountPacked_V16
{
	unsigned int Count : 24;
	unsigned int Type : 8;
};

struct VGMesh_t_v16
{
	uint64_t flags;
	int vertexCount;
	short vertexSize;
	short unk;
	int indexOffset;
	RMdlVGIndexCountPacked_V16 indexPacked;
	int vertexOffset;
	int vertexBufferSize;

	int weightsOffset;
	int weightsCount;

	int unkOffset;
	int unkCount;
};

struct RMdlVGMesh
{
	uint32_t Flags1;					// Flags that pertain to this mesh
	uint32_t Flags2;					// Also flags that pertain to this mesh
	uint32_t VertexBufferStride;		// Stride in bytes of the vertex buffer
	uint32_t VertexCount;				// Count of vertices used

	uint64_t IndexOffset;
	RMdlVGIndexCountPacked IndexPacked;	// 0x2 each (uint16_t)

	uint64_t VertexOffset;
	uint64_t VertexCountBytes;		// 0x1 each aka, in bytes

	uint64_t ExtendedWeightsOffset;
	uint64_t ExtendedWeightsCount;		// Only 1 byte per count

	uint64_t ExternalWeightsOffset;
	uint64_t ExternalWeightsCount;	// 0x10 each

	uint64_t StripsOffset;
	uint64_t StripsCount;			// 0x23 each
};

struct RMdlVGMesh_V14
{
	uint32_t Flags1;					// Flags that pertain to this mesh
	uint32_t Flags2;					// Also flags that pertain to this mesh
	uint32_t VertexBufferStride;		// Stride in bytes of the vertex buffer
	uint32_t VertexCount;				// Count of vertices used

	uint64_t IndexOffset;
	RMdlVGIndexCountPacked IndexPacked;	// 0x2 each (uint16_t)

	uint64_t VertexOffset;
	uint64_t VertexCountBytes;		// 0x1 each aka, in bytes

	uint64_t ExtendedWeightsOffset;
	uint64_t ExtendedWeightsCount; // idk if these are actually unused but it looks like they are

	uint64_t ExternalWeightsOffset;
	uint64_t ExternalWeightsCount;	// 0x10 each

	uint64_t StripsOffset;
	uint64_t StripsCount;			// 0x23 each

	uint64_t UnkOffset;
	uint64_t UnkCount;		// Only 1 byte per count
};

struct RMdlVGMeshOld
{
	uint32_t Flags1;					// Flags that pertain to this mesh
	uint32_t Flags2;					// Also flags that pertain to this mesh
	uint32_t VertexOffsetBytes;			// Offset into vertex buffer by bytes
	uint32_t VertexBufferStride;		// Stride in bytes of the vertex buffer
	uint32_t VertexCount;				// Count of vertices used
	uint32_t Int6;
	uint32_t ExtendedWeightsOffset;		// Offset into the extended weights buffer
	uint32_t ExtendedWeightsSize;		// Size or count of extended weights used by this mesh
	uint32_t IndexOffset;				// Some form of index offset
	uint32_t IndexCount;				// Some form of index count
	uint32_t VertexOffset2;				// Some form of vertex offset (Not always used??)
	uint32_t VertexCount2;				// some form of vertex count
	uint32_t StripsIndex;				// Index into the strips structs
	uint32_t StripsCount;
	uint32_t Int15;
	uint32_t Int16;
	uint32_t Int17;
	uint32_t Int18;
};

struct RMdlPhyHeader
{
	uint32_t HeaderSize;
	uint32_t Id;
	uint32_t SolidCount;
	uint32_t Checksum;
	uint32_t TextOffset; // offset to the text section
};

#define LAST_IND(x,part_type)    (sizeof(x)/sizeof(part_type) - 1)
#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN
#  define LOW_IND(x,part_type)   LAST_IND(x,part_type)
#  define HIGH_IND(x,part_type)  0
#else
#  define HIGH_IND(x,part_type)  LAST_IND(x,part_type)
#  define LOW_IND(x,part_type)   0
#endif

#define BYTEn(x, n)   (*((uint8_t*)&(x)+n))
#define WORDn(x, n)   (*((uint16_t*)&(x)+n))
#define DWORDn(x, n)  (*((uint32_t*)&(x)+n))
#define LOBYTE(x)  BYTEn(x,LOW_IND(x,uint8_t))
#define LOWORD(x)  WORDn(x,LOW_IND(x,uint16_t))
#define LODWORD(x) DWORDn(x,LOW_IND(x,uint32_t))
#define HIBYTE(x)  BYTEn(x,HIGH_IND(x,uint8_t))
#define HIWORD(x)  WORDn(x,HIGH_IND(x,uint16_t))
#define HIDWORD(x) DWORDn(x,HIGH_IND(x,uint32_t))
#define BYTE1(x)   BYTEn(x,  1)         // byte 1 (counting from 0)
#define BYTE2(x)   BYTEn(x,  2)

struct RMdlPackedVertexPosition
{
	uint32_t _Value[2];

	Math::Vector3 Unpack()
	{
		float x, y, z;

		x = ((_Value[0] & 0x1FFFFF) * 0.0009765625) - 1024.0;
		y = ((((_Value[1] & 0x3FFu) << 11) + (_Value[0] >> 21)) * 0.0009765625) - 1024.0;
		z = ((_Value[1] >> 10) * 0.0009765625) - 2048.0;

		return Math::Vector3(x, y, z);
	}
};

struct RMdlPackedVertexTBN
{
	uint32_t _Value;

	// uint32_t _Value = 0b11011111111111111111110000110010;
	//                     ^
	//        binorm sign==|^^
	//norm dropped component||^
	//             norm sign==|^^^^^^^^^
	//                           norm1  ^^^^^^^^^
	//                                    norm2  ^^^^^^^^^^
	//                                           packedTangent

	// 140455C30
	Math::Vector3 UnpackNormal()
	{
		Math::Vector3 nml;

		// check sign bit on first component
		bool sign = (_Value >> 28) & 1;

		float val1 = sign ? -255.f : 255.f; // normal value 1
		float val2 = ((_Value >> 19) & 0x1FF) - 256.f;
		float val3 = ((_Value >> 10) & 0x1FF) - 256.f;

		int idx1 = (_Value >> 29) & 3;
		int idx2 = (0x124u >> (2 * idx1 + 2)) & 3;
		int idx3 = (0x124u >> (2 * idx1 + 4)) & 3;

		// normalise the normal
		float normalised = 1.0 / sqrtf((255.0 * 255.0) + (val2 * val2) + (val3 * val3));

		nml[idx1] = val1 * normalised;
		nml[idx2] = val2 * normalised;
		nml[idx3] = val3 * normalised;

		return nml;
	}

	Math::Vector3 UnpackTangent(Math::Vector3 Normal)
	{
		float r2y = 1 + Normal.Z;
		r2y = 1.f / r2y;
		float r2z = -r2y * Normal.X;
		float r2w = Normal.Y * Normal.Y;
		float r3x = r2z * Normal.Y;
		float r4x = -r2y * r2w + 1;
		float r4y = -Normal.X;
		float r4z = -Normal.Y;
		float r3z = r2z * Normal.X + 1;
		float r3y;
		float r3w = r4y;

		Vector3 r2;
		if (Normal.Z < -0.999899983)
		{
			r2.X = 0;
			r2.Y = -1;
			r2.Z = 0;
		}
		else
		{
			r2.X = r3z;
			r2.Y = r3x;
			r2.Z = r3w;
		}

		Vector3 r3;
		float r4w = r3x;
		if (Normal.Z < -0.999899983)
		{
			r3.X = -1;
			r3.Y = 0;
			r3.Z = 0;
		}
		else
		{
			r3.X = r4w;
			r3.Y = r4x;
			r3.Z = r4z;
		}

		float x = (_Value & 1023) * 0.00614192151;
		float r2x = sin(x);
		r4x = cos(x);

		r3x *= r2x;
		r3y *= r2x;
		r3z *= r2x;

		r2x = r2y * r4x + r3x;
		r2y = r2z * r4x + r3y;
		r2z = r2w * r4x + r3z;

		// normalizing
		float r1w = r2x * r2x + r2y * r2y + r2z * r2z;
		r1w = 1.f / sqrt(r1w);
		r2x *= r1w;
		r2y *= r1w;
		r2z *= r1w;

		return Math::Vector3(r2x, r2y, r2z);
	}

	int8_t GetBitangentSign()
	{
		return _Value >> 31 ? -1 : 1;
	}
};

struct RMdlPackedVertexWeights
{
	uint16_t BlendWeights[2];
	uint8_t BlendIds[4];
};

struct mstudiobonev54_t
{
	int sznameindex;

	int parent; // parent bone
	int bonecontroller[6]; // bone controller index, -1 == none

	// default values
	Vector3 pos;
	Math::Quaternion quat;
	Math::Vector3 rot; // radianeuler

	Vector3 unkvector; // the same as whatever v53 is

	matrix3x4_t poseToBone;
	Math::Quaternion qAlignment;

	int flags;
	int proctype;
	int procindex; // procedural rule
	int physicsbone; // index into physically simulated bone

	int surfacepropidx; // index into string tablefor property name

	int contents; // See BSPFlags.h for the contents flags

	int unk; // similar value in studiohdr

	int surfacepropLookup; // unsure, the normal spot has data though

	int unkid; // id is for unk section after stringblock, lacks toggle
};

struct mstudioattachmentv54_t
{
	int sznameindex;
	int	flags;

	int	localbone; // parent bone

	matrix3x4_t	localmatrix; // attachment point
};

// $hboxset, $hbox
struct mstudiohitboxset_t
{
	int sznameindex;
	int numhitboxes;
	int hitboxindex;
};

struct mstudiobboxv54_t
{
	int bone;
	int group; // intersection group

	Vector3 bbmin; // bounding box
	Vector3 bbmax;

	int szhitboxnameindex; // offset to the name of the hitbox.

	int unk;
	int keyvalueindex; // used for KV names in string block, should be set to hitboxname if kv unneeded.
};

struct mstudio_meshvertexdata_t
{
	int unk;
	int numLODVertexes[8];
};

struct mstudiomesh_s3_t
{
	int material;

	int modelindex;

	int numvertices; // number of unique vertices/normals/texcoords
	int vertexoffset; // vertex mstudiovertex_t

	// Access thin/fat mesh vertex data (only one will return a non-NULL result)

	int numflexes; // vertex animation
	int flexindex;

	// special codes for material operations
	int materialtype;
	int materialparam;

	// a unique ordinal for this mesh
	int meshid;

	Vector3 center;

	mstudio_meshvertexdata_t vertexdata;

	int unused[2];
};

struct mstudiomesh_v121_t
{
	int material;

	int modelindex;

	int numvertices; // number of unique vertices/normals/texcoords
	int vertexoffset; // vertex mstudiovertex_t

	// a unique ordinal for this mesh
	int meshid;

	Vector3 center;

	// a unique ordinal for this mesh
	//int meshid;

	mstudio_meshvertexdata_t vertexdata;

	int unused[2];
};

struct mstudiomesh_t_v16
{
	short material;

	// a unique ordinal for this mesh
	short meshid;

	//short modelindex;

	byte unk[4];

	//short numvertices; // number of unique vertices/normals/texcoords
	//short vertexoffset; // vertex mstudiovertex_t

	Vector3 center;
};

struct mstudiobodyparts_t_v16
{
	short sznameindex;
	uint16 modelindex;
	int base;
	int nummodels;
	int meshindex; // index into models array
};

struct mstudiotexturev54_t
{
	int sznameindex;
	uint64_t guid;
};

// fake studio struct
struct mstudiomaterial_t
{
	uint64_t guid;
	string name;

};

struct mstudiotexture_t_v16
{
	uint64_t guid;
};
#pragma pack(pop)

// Game helper structs
struct RMdlFixupPatches
{
	List<mstudiomaterial_t>* Materials;
	List<uint8_t>* BoneRemaps;
	string MaterialPath;

	uint64_t VertexShift;
	uint64_t MeshOffset;
	uint64_t WeightsTableOffset;
};

struct RMdlMaterial
{
	string MaterialName;
	string FullMaterialName; // made another var so i dont break things somehow

	string AlbedoMapName;
	string NormalMapName;
	string GlossMapName;
	string SpecularMapName;
	string EmissiveMapName;
	string AmbientOcclusionMapName;
	string CavityMapName;

	uint64_t AlbedoHash;
	uint64_t NormalHash;
	uint64_t GlossHash;
	uint64_t SpecularHash;
	uint64_t EmissiveHash;
	uint64_t AmbientOcclusionHash;
	uint64_t CavityHash;
};


//============
// VVD (IDSV)
//============

namespace vvd
{
	struct Vector4_t
	{
		float x, y, z, w;
	};

	struct mstudioweightextra_t
	{
		short weight[3]; // value divided by 32767.0

		short pad; // likely alignment

		int extraweightindex;
	};

	struct mstudioboneweight_t
	{
		union
		{
			float	weight[MAX_NUM_BONES_PER_VERT];
			mstudioweightextra_t weightextra;
		};

		unsigned char bone[MAX_NUM_BONES_PER_VERT]; // set to unsigned so we can read it
		byte	numbones;
	};

	struct mstudiovertex_t
	{
		mstudioboneweight_t	m_BoneWeights;
		Vector3 m_vecPosition;
		Vector3 m_vecNormal;
		Vector2 m_vecTexCoord;
	};

	struct vertexFileFixup_t
	{
		int		lod;				// used to skip culled root lod
		int		sourceVertexID;		// absolute index from start of vertex/tangent blocks
		int		numVertexes;
	};

	struct vertexFileHeader_t
	{
		int id; // MODEL_VERTEX_FILE_ID
		int version; // MODEL_VERTEX_FILE_VERSION
		int checksum; // same as studiohdr_t, ensures sync

		int numLODs; // num of valid lods
		int numLODVertexes[MAX_NUM_LODS]; // num verts for desired root lod

		int numFixups; // num of vertexFileFixup_t

		int fixupTableStart; // offset from base to fixup table
		int vertexDataStart; // offset from base to vertex block
		int tangentDataStart; // offset from base to tangent block

		vertexFileFixup_t* fixup(int i)
		{
			return reinterpret_cast<vertexFileFixup_t*>((char*)this + fixupTableStart) + i;
		}

		mstudiovertex_t* vertex(int i)
		{
			return reinterpret_cast<mstudiovertex_t*>((char*)this + vertexDataStart) + i;
		}

		Vector4_t* tangent(int i)
		{
			return reinterpret_cast<Vector4_t*>((char*)this + tangentDataStart) + i;
		}
	};
}


//============
// VVC (IDCV)
//============

namespace vvc
{
	struct VertexColor_t
	{
		uint8 r, g, b, a;
	};


	struct vertexColorFileHeader_t
	{
		int id; // MODEL_VERTEX_FILE_ID
		int version; // MODEL_VERTEX_FILE_VERSION
		int checksum; // same as studiohdr_t, ensures sync

		int numLODs; // num of valid lods
		int numLODVertexes[MAX_NUM_LODS]; // num verts for desired root lod

		int colorDataStart;
		int uv2DataStart;

		VertexColor_t* color(int i)
		{
			return reinterpret_cast<VertexColor_t*>((char*)this + colorDataStart) + i;
		}

		Vector2* uv2(int i)
		{
			return reinterpret_cast<Vector2*>((char*)this + uv2DataStart) + i;
		}
	};
}


//============
// 'VVW'
//============

namespace vvw
{
	struct mstudioboneweightextra_t
	{
		short	weight; // weight = this / 32767.0
		short   bone;
	};

	struct vertexBoneWeightsExtraFileHeader_t
	{
		int checksum; // same as studiohdr_t, ensures sync
		int version; // should be 1

		int numLODVertexes[MAX_NUM_LODS]; // maybe this but the others don't get filled?

		int weightDataStart; // index into mstudioboneweightextra_t array

		// test these funcs
		mstudioboneweightextra_t* GetWeightData(int i)
		{
			return (mstudioboneweightextra_t*)(((char*)this + weightDataStart) + i);
		}
	};
}


//============
// VTX
//============

// we have odd sized structs
#pragma pack(push, 1)
namespace vtx
{
	struct Vertex_t
	{
		// these index into the mesh's vert[origMeshVertID]'s bones
		unsigned char boneWeightIndex[MAX_NUM_BONES_PER_VERT];
		unsigned char numBones;

		unsigned short origMeshVertID;

		// for sw skinned verts, these are indices into the global list of bones
		// for hw skinned verts, these are hardware bone indices
		char boneID[MAX_NUM_BONES_PER_VERT];
	};

	enum StripHeaderFlags_t
	{
		STRIP_IS_TRILIST = 0x01,
		STRIP_IS_QUADLIST_REG = 0x02,		// Regular sub-d quads
		STRIP_IS_QUADLIST_EXTRA = 0x04		// Extraordinary sub-d quads
	};


	// A strip is a piece of a stripgroup which is divided by bones 
	struct StripHeader_t
	{
		int numIndices;
		int indexOffset;

		int numVerts;
		int vertOffset;

		short numBones;

		unsigned char flags;

		int numBoneStateChanges;
		int boneStateChangeOffset;

		// MDL Version 49 and up only
		int numTopologyIndices;
		int topologyOffset;
	};

	enum StripGroupFlags_t
	{
		STRIPGROUP_IS_HWSKINNED = 0x02,
		STRIPGROUP_IS_DELTA_FLEXED = 0x04,
		STRIPGROUP_SUPPRESS_HW_MORPH = 0x08,	// NOTE: This is a temporary flag used at run time.
	};

	struct StripGroupHeader_t
	{
		// These are the arrays of all verts and indices for this mesh.  strips index into this.
		int numVerts;
		int vertOffset;

		Vertex_t* vtxVert(int i)
		{
			return reinterpret_cast<Vertex_t*>((char*)this + vertOffset) + i;
		}

		int numIndices;
		int indexOffset;

		unsigned short* vtxIndice(int i)
		{
			return reinterpret_cast<unsigned short*>((char*)this + indexOffset) + i;
		}

		int numStrips;
		int stripOffset;

		StripHeader_t* vtxStrip(int i)
		{
			return reinterpret_cast<StripHeader_t*>((char*)this + stripOffset) + i;
		}

		unsigned char flags;

		// The following fields are only present if MDL version is >=49
		// Points to an array of unsigned shorts (16 bits each)
		int numTopologyIndices;
		int topologyOffset;
	};

	struct MeshHeader_t
	{
		int numStripGroups;
		int stripGroupHeaderOffset;

		unsigned char flags; // never read these as eyeballs and mouths are depreciated

		StripGroupHeader_t* vtxStripGrp(int i)
		{
			return reinterpret_cast<StripGroupHeader_t*>((char*)this + stripGroupHeaderOffset) + i;
		}
	};

	struct ModelLODHeader_t
	{
		//Mesh array
		int numMeshes;
		int meshOffset;

		float switchPoint;

		MeshHeader_t* vtxMesh(int i)
		{
			return reinterpret_cast<MeshHeader_t*>((char*)this + meshOffset) + i;
		}
	};

	struct ModelHeader_t
	{
		//LOD mesh array
		int numLODs;   //This is also specified in FileHeader_t
		int lodOffset;

		ModelLODHeader_t* vtxLOD(int i)
		{
			return reinterpret_cast<ModelLODHeader_t*>((char*)this + lodOffset) + i;
		}
	};

	struct BodyPartHeader_t
	{
		// Model array
		int numModels;
		int modelOffset;

		ModelHeader_t* vtxModel(int i)
		{
			return reinterpret_cast<ModelHeader_t*>((char*)this + modelOffset) + i;
		}
	};

	struct FileHeader_t
	{
		// file version as defined by OPTIMIZED_MODEL_FILE_VERSION (currently 7)
		int version;

		// hardware params that affect how the model is to be optimized.
		int vertCacheSize;
		unsigned short maxBonesPerStrip;
		unsigned short maxBonesPerFace;
		int maxBonesPerVert;

		// must match checkSum in the .mdl
		int checkSum;

		int numLODs; // Also specified in ModelHeader_t's and should match

		// Offset to materialReplacementList Array. one of these for each LOD, 8 in total
		int materialReplacementListOffset;

		// Defines the size and location of the body part array
		int numBodyParts;
		int bodyPartOffset;

		BodyPartHeader_t* vtxBodypart(int i)
		{
			return reinterpret_cast<BodyPartHeader_t*>((char*)this + bodyPartOffset) + i;
		}
	};
}
#pragma pack(pop)

//============
// 'VG' (0tVG)
//============
namespace vg
{
	struct ModelLODHeader_t
	{
		//Mesh array
		short meshIndex;
		short numMeshes;

		float switchPoint;
	};
}
// soon tm


//============
// MDL (IDST)
//============

//union mstudioanimvalue_t
//{
//	struct
//	{
//		byte	valid;
//		byte	total;
//	} num;
//	short		value;
//};
//
//struct mstudioanim_valueptr_t
//{
//	short	offset[3];
//
//	// untested
//	mstudioanimvalue_t* mdlAnimValue(int i)
//	{
//		return reinterpret_cast<mstudioanimvalue_t*>((char*)this + offset[i]);
//	}
//};

struct mstudiobodyparts_t
{
	int sznameindex;
	int nummodels;
	int base;
	int modelindex;
};

struct mstudio_meshvertexloddata_t
{
	int modelvertexdataUnusedPad; // likely has none of the funny stuff because unused

	int numLODVertexes[MAX_NUM_LODS]; // depreciated starting with rmdl v14(?)
};

namespace titanfall2
{
	struct mstudiotexture_t
	{
		int sznameindex;

		char* textureName()
		{
			return ((char*)this + sznameindex);
		}

		int unused_flags;
		int used;

		int unused[8];
	};

	struct mstudiomesh_t
	{
		int material;

		int modelindex;

		int numvertices; // number of unique vertices/normals/texcoords
		int vertexoffset; // vertex mstudiovertex_t
						  // offset by vertexoffset number of verts into vvd vertexes, relative to the models offset

		// Access thin/fat mesh vertex data (only one will return a non-NULL result)

		int deprecated_numflexes; // vertex animation
		int deprecated_flexindex;

		// special codes for material operations
		int deprecated_materialtype;
		int deprecated_materialparam;

		// a unique ordinal for this mesh
		int meshid;

		Vector3 center;

		mstudio_meshvertexloddata_t vertexloddata;

		int unk[2]; // set on load

		int unused[6]; // remove as appropriate
	};

	struct mstudiomodel_t
	{
		char name[64];

		int type;

		float boundingradius;

		int nummeshes;
		int meshindex;

		titanfall2::mstudiomesh_t* mdlMesh(int i)
		{
			return reinterpret_cast<titanfall2::mstudiomesh_t*>((char*)this + meshindex) + i;
		}

		// cache purposes
		int numvertices; // number of unique vertices/normals/texcoords
		int vertexindex; // vertex Vector
						 // offset by vertexindex number of bytes into vvd verts
		int tangentsindex; // tangents Vector
						   // offset by tangentsindex number of bytes into vvd tangents

		int numattachments;
		int attachmentindex;

		int deprecated_numeyeballs;
		int deprecated_eyeballindex;

		int pad[4];

		int colorindex; // vertex color
						// offset by colorindex number of bytes into vvc vertex colors
		int uv2index; // vertex second uv map
					  // offset by uv2index number of bytes into vvc secondary uv map

		int unused[4];
	};

	struct mstudiobodyparts_t
	{
		int sznameindex;
		int nummodels;
		int base;
		int modelindex;

		titanfall2::mstudiomodel_t* mdlModel(int i)
		{
			return reinterpret_cast<titanfall2::mstudiomodel_t*>((char*)this + modelindex) + i;
		}
	};

	struct mstudioanim_valueptr_t
	{
		short	offset[3];
		inline mstudioanimvalue_t* pAnimvalue(int i)
			const {
			return (offset[i] > 0) ? reinterpret_cast<mstudioanimvalue_t*>((char*)this + offset[i]) : nullptr;
		}
	};

	// v53 flags
	// These work as toggles, flag enabled = raw data, flag disabled = pointers
	#define STUDIO_ANIM_DELTA		0x01 // delta animation
	#define STUDIO_ANIM_RAWPOS		0x02 // Vector48
	#define STUDIO_ANIM_RAWROT		0x04 // Quaternion64
	#define STUDIO_ANIM_RAWSCALE	0x08 // Vector48, drone_frag.mdl for scale track usage
	#define STUDIO_ANIM_NOROT		0x10

	struct mstudio_rle_anim_t
	{
		float				posscale; // does what posscale is used for

		unsigned char		bone;
		byte				flags;		// weighing options

		// broken
		inline char* pData() const { return ((char*)this + sizeof(mstudio_rle_anim_t)); } // gets start of animation data, this should have a '+2' if aligned to 4
		mstudioanim_valueptr_t* pRotV() const { return reinterpret_cast<mstudioanim_valueptr_t*>(pData()); } // returns rot as mstudioanim_valueptr_t
		mstudioanim_valueptr_t* pPosV() const { return reinterpret_cast<mstudioanim_valueptr_t*>(pData() + 8); } // returns pos as mstudioanim_valueptr_t
		mstudioanim_valueptr_t* pScaleV() const { return reinterpret_cast<mstudioanim_valueptr_t*>(pData() + 14); } // returns scale as mstudioanim_valueptr_t

		Quaternion64* pQuat64() const { return reinterpret_cast<Quaternion64*>(pData()); } // returns rot as static Quaternion64
		Vector48* pPos() const { return reinterpret_cast<Vector48*>(pData() + 8); } // returns pos as static Vector48
		Vector48* pScale() const { return reinterpret_cast<Vector48*>(pData() + 14); } // returns scale as static Vector48

		// points to next bone in the list
		int* pNextOffset() const { return reinterpret_cast<int*>(pData() + 20); }
	};

	struct mstudioanimsections_t
	{
		int animindex;
	};

	#define STUDIO_X		0x00000001
	#define STUDIO_Y		0x00000002	
	#define STUDIO_Z		0x00000004
	#define STUDIO_XR		0x00000008
	#define STUDIO_YR		0x00000010
	#define STUDIO_ZR		0x00000020

	#define STUDIO_LX		0x00000040
	#define STUDIO_LY		0x00000080
	#define STUDIO_LZ		0x00000100
	#define STUDIO_LXR		0x00000200
	#define STUDIO_LYR		0x00000400
	#define STUDIO_LZR		0x00000800

	#define STUDIO_LINEAR	0x00001000

	#define STUDIO_TYPES	0x0003FFFF
	#define STUDIO_RLOOP	0x00040000	// controller that wraps shortest distance

	struct mstudiomovement_t
	{
		int					endframe;
		int					motionflags;
		float				v0;			// velocity at start of block
		float				v1;			// velocity at end of block
		float				angle;		// YAW rotation at end of this blocks movement
		Vector3				vector;		// movement vector relative to this blocks initial angle
		Vector3				position;	// relative to start of animation???
	};

	// new in Titanfall 1
	// translation track for origin bone, used in lots of animated scenes, requires STUDIO_FRAMEMOVEMENT
	// pos_x, pos_y, pos_z, yaw
	struct mstudioframemovement_t
	{
		float scale[4];
		short offset[4];
		inline mstudioanimvalue_t* pAnimvalue(int i) const { return (offset[i] > 0) ? reinterpret_cast<mstudioanimvalue_t*>((char*)this + offset[i]) : nullptr; }
	};

	struct mstudioanimdesc_t
	{
		int baseptr;

		int sznameindex;
		inline const char* pszName() const { return ((char*)this + sznameindex); }

		float fps; // frames per second	
		int flags; // looping/non-looping flags

		int numframes;

		// piecewise movement
		int nummovements;
		int movementindex;
		inline mstudiomovement_t* const pMovement(int i) const { return reinterpret_cast<mstudiomovement_t*>((char*)this + movementindex) + i; };

		int framemovementindex; // new in v52
		inline const mstudioframemovement_t* pFrameMovement() const { return reinterpret_cast<mstudioframemovement_t*>((char*)this + framemovementindex); }

		int animindex; // non-zero when anim data isn't in sections
		//mstudio_rle_anim_t* pAnim(int* piFrame, float& flStall) const; // returns pointer to data and new frame index
		mstudio_rle_anim_t* pAnim(int* piFrame) const; // returns pointer to data and new frame index

		int numikrules;
		int ikruleindex; // non-zero when IK data is stored in the mdl

		int numlocalhierarchy;
		int localhierarchyindex;

		int sectionindex;
		int sectionframes; // number of frames used in each fast lookup section, zero if not used
		inline const mstudioanimsections_t* pSection(int i) const { return reinterpret_cast<mstudioanimsections_t*>((char*)this + sectionindex) + i; }

		int unused[8];
	};

	struct mstudiolinearbone_t
	{
		int numbones;

		int flagsindex;
		int* pFlags(int i)
		const {
			assert(i >= 0 && i < numbones);
			return reinterpret_cast<int*>((char*)this + flagsindex) + i;
		}

		int	parentindex;
		int* pParent(int i)
		const {
			assert(i >= 0 && i < numbones);
			return reinterpret_cast<int*>((char*)this + parentindex) + i;
		}

		int	posindex;
		const Vector3* pPos(int i)
		const {
			assert(i >= 0 && i < numbones);
			return reinterpret_cast<Vector3*>((char*)this + posindex) + i;
		}

		int quatindex;
		const Quaternion* pQuat(int i)
			const {
			assert(i >= 0 && i < numbones);
			return reinterpret_cast<Quaternion*>((char*)this + quatindex) + i;
		}

		int rotindex;
		const RadianEuler* pRot(int i)
			const {
			assert(i >= 0 && i < numbones);
			return reinterpret_cast<RadianEuler*>((char*)this + rotindex) + i;
		}

		int posetoboneindex;
		const matrix3x4_t* pPoseToBone(int i)
			const {
			assert(i >= 0 && i < numbones);
			return reinterpret_cast<matrix3x4_t*>((char*)this + posetoboneindex) + i;
		}

		int	posscaleindex; // unused
		/*const Vector3* pPosScale(int i)
			const {
			assert(i >= 0 && i < numbones);
			return reinterpret_cast<Vector3*>((char*)this + posscaleindex) + i;
		}*/

		int	rotscaleindex;
		const Vector3* pRotScale(int i)
			const {
			assert(i >= 0 && i < numbones);
			return reinterpret_cast<Vector3*>((char*)this + rotscaleindex) + i;
		}

		int	qalignmentindex;
		const Quaternion* pQAlignment(int i)
		const {
			assert(i >= 0 && i < numbones);
			return reinterpret_cast<Quaternion*>((char*)this + qalignmentindex) + i;
		}

		int unused[6];
	};

	#define BONE_CALCULATE_MASK			0x1F
	#define BONE_PHYSICALLY_SIMULATED	0x01	// bone is physically simulated when physics are active
	#define BONE_PHYSICS_PROCEDURAL		0x02	// procedural when physics is active
	#define BONE_ALWAYS_PROCEDURAL		0x04	// bone is always procedurally animated
	#define BONE_SCREEN_ALIGN_SPHERE	0x08	// bone aligns to the screen, not constrained in motion.
	#define BONE_SCREEN_ALIGN_CYLINDER	0x10	// bone aligns to the screen, constrained by it's own axis.

	#define BONE_USED_BY_IKCHAIN		0x20 // bone is influenced by IK chains, added in V52 (Titanfall 1)

	#define BONE_USED_MASK				0x0007FF00
	#define BONE_USED_BY_ANYTHING		0x0007FF00
	#define BONE_USED_BY_HITBOX			0x00000100	// bone (or child) is used by a hit box
	#define BONE_USED_BY_ATTACHMENT		0x00000200	// bone (or child) is used by an attachment point
	#define BONE_USED_BY_VERTEX_MASK	0x0003FC00
	#define BONE_USED_BY_VERTEX_LOD0	0x00000400	// bone (or child) is used by the toplevel model via skinned vertex
	#define BONE_USED_BY_VERTEX_LOD1	0x00000800	
	#define BONE_USED_BY_VERTEX_LOD2	0x00001000  
	#define BONE_USED_BY_VERTEX_LOD3	0x00002000
	#define BONE_USED_BY_VERTEX_LOD4	0x00004000
	#define BONE_USED_BY_VERTEX_LOD5	0x00008000
	#define BONE_USED_BY_VERTEX_LOD6	0x00010000
	#define BONE_USED_BY_VERTEX_LOD7	0x00020000
	#define BONE_USED_BY_BONE_MERGE		0x00040000	// bone is available for bone merge to occur against it

	#define BONE_FLAG_UNK				0x00080000 // where?

	#define BONE_USED_BY_VERTEX_AT_LOD(lod) ( BONE_USED_BY_VERTEX_LOD0 << (lod) )
	#define BONE_USED_BY_ANYTHING_AT_LOD(lod) ( ( BONE_USED_BY_ANYTHING & ~BONE_USED_BY_VERTEX_MASK ) | BONE_USED_BY_VERTEX_AT_LOD(lod) )

	#define BONE_TYPE_MASK				0x00F00000
	#define BONE_FIXED_ALIGNMENT		0x00100000	// bone can't spin 360 degrees, all interpolation is normalized around a fixed orientation

	#define BONE_HAS_SAVEFRAME_POS		0x00200000	// Vector48
	#define BONE_HAS_SAVEFRAME_ROT64	0x00400000	// Quaternion64
	#define BONE_HAS_SAVEFRAME_ROT32	0x00800000	// Quaternion32

	#define BONE_FLAG_UNK1				0x01000000 // where?

	struct mstudiobone_t
	{
		int sznameindex;

		char* pszName()
		{
			return ((char*)this + sznameindex);
		}

		int parent; // parent bone
		int bonecontroller[6]; // bone controller index, -1 == none

		// default values
		Vector3 pos; // base bone position
		Quaternion quat;
		RadianEuler rot; // base bone rotation
		Vector3 scale; // bone scale(?)

		// compression scale
		Vector3 posscale; // scale muliplier for bone position in animations. depreciated in v53, as the posscale is stored in anim bone headers
		Vector3 rotscale; // scale muliplier for bone rotation in animations
		Vector3 scalescale; // scale muliplier for scale

		matrix3x4_t poseToBone;
		Quaternion qAlignment;

		int flags;
		int proctype;
		int procindex; // procedural rule offset
		int physicsbone; // index into physically simulated bone

		int surfacepropidx; // index into string tablefor property name

		int contents; // See BSPFlags.h for the contents flags

		int surfacepropLookup; // this index must be cached by the loader, not saved in the file

		// unknown phy related section
		short unkindex; // index into this section
		short unkcount; // number of sections for this bone?? see: models\s2s\s2s_malta_gun_animated.mdl

		int unused[7]; // remove as appropriate
	};

	struct studiohdr_t
	{
		int id; // Model format ID, such as "IDST" (0x49 0x44 0x53 0x54)
		int version; // Format version number, such as 48 (0x30,0x00,0x00,0x00)
		int checksum; // This has to be the same in the phy and vtx files to load!
		int sznameindex; // This has been moved from studiohdr2 to the front of the main header.
		inline char* pszName() const { return ((char*)this + sznameindex); }
		char name[64]; // The internal name of the model, padding with null bytes.
						// Typically "my_model.mdl" will have an internal name of "my_model"
		int length; // Data size of MDL file in bytes.

		Vector3 eyeposition;	// ideal eye position

		Vector3 illumposition;	// illumination center

		Vector3 hull_min;		// ideal movement hull size
		Vector3 hull_max;

		Vector3 view_bbmin;		// clipping bounding box
		Vector3 view_bbmax;

		int flags;

		// highest observed: 250
		// max is definitely 256 because 8bit uint limit
		int numbones; // bones
		int boneindex;
		mstudiobone_t* pBone(int i)
			const {
			assert(i >= 0 && i < numbones);
			return reinterpret_cast<mstudiobone_t*>((char*)this + boneindex) + i;
		}

		int numbonecontrollers; // bone controllers
		int bonecontrollerindex;

		int numhitboxsets;
		int hitboxsetindex;

		int numlocalanim; // animations/poses
		int localanimindex; // animation descriptions

		titanfall2::mstudioanimdesc_t* pAnimdesc(int i)
		{
			return reinterpret_cast<titanfall2::mstudioanimdesc_t*>((char*)this + localanimindex) + i;
		}

		int numlocalseq; // sequences
		int	localseqindex;

		int activitylistversion; // initialization flag - have the sequences been indexed? set on load
		int eventsindexed;

		// mstudiotexture_t
		// short rpak path
		// raw textures
		int numtextures; // the material limit exceeds 128, probably 256.
		int textureindex;

		titanfall2::mstudiotexture_t* texture(int i)
		{
			return reinterpret_cast<titanfall2::mstudiotexture_t*>((char*)this + textureindex) + i;
		}

		// this should always only be one, unless using vmts.
		// raw textures search paths
		int numcdtextures;
		int cdtextureindex;

		// replaceable textures tables
		int numskinref;
		int numskinfamilies;
		int skinindex;

		int numbodyparts;
		int bodypartindex;

		mstudiobodyparts_t* mdlBodypart(int i)
		{
			return reinterpret_cast<mstudiobodyparts_t*>((char*)this + bodypartindex) + i;
		}

		int numlocalattachments;
		int localattachmentindex;

		int numlocalnodes;
		int localnodeindex;
		int localnodenameindex;

		int deprecated_numflexdesc;
		int deprecated_flexdescindex;

		int deprecated_numflexcontrollers;
		int deprecated_flexcontrollerindex;

		int deprecated_numflexrules;
		int deprecated_flexruleindex;

		int numikchains;
		int ikchainindex;

		int numruimeshes;
		int ruimeshindex;

		int numlocalposeparameters;
		int localposeparamindex;

		int surfacepropindex;

		int keyvalueindex;
		int keyvaluesize;

		int numlocalikautoplaylocks;
		int localikautoplaylockindex;

		float mass;
		int contents;

		// external animations, models, etc.
		int numincludemodels;
		int includemodelindex;

		int virtualModel; // should be void

		// animblock is either completely cut, this is because they no longer use .ani files.

		int bonetablebynameindex;

		// if STUDIOHDR_FLAGS_CONSTANT_DIRECTIONAL_LIGHT_DOT is set,
		// this value is used to calculate directional components of lighting 
		// on static props
		byte constdirectionallightdot;

		// set during load of mdl data to track *desired* lod configuration (not actual)
		// the *actual* clamped root lod is found in studiohwdata
		// this is stored here as a global store to ensure the staged loading matches the rendering
		byte rootLOD;

		// set in the mdl data to specify that lod configuration should only allow first numAllowRootLODs
		// to be set as root LOD:
		//	numAllowedRootLODs = 0	means no restriction, any lod can be set as root lod.
		//	numAllowedRootLODs = N	means that lod0 - lod(N-1) can be set as root lod, but not lodN or lower.
		byte numAllowedRootLODs;

		byte unused;

		float fadeDistance; // set to -1 to never fade. set above 0 if you want it to fade out, distance is in feet.
							// player/titan models seem to inherit this value from the first model loaded in menus.
							// works oddly on entities, probably only meant for static props

		int deprecated_numflexcontrollerui;
		int deprecated_flexcontrolleruiindex;

		float flVertAnimFixedPointScale;
		int surfacepropLookup; // this index must be cached by the loader, not saved in the file

		// this is in all shipped models, probably part of their asset bakery. it should be 0x2CC.
		// doesn't actually need to be written pretty sure, only four bytes when not present.
		// this is not completely true as some models simply have nothing, such as animation models.
		int sourceFilenameOffset;

		int numsrcbonetransform;
		int srcbonetransformindex;

		int	illumpositionattachmentindex;

		int linearboneindex;
		mstudiolinearbone_t* pLinearBones() const { return linearboneindex ? reinterpret_cast<mstudiolinearbone_t*>((char*)this + linearboneindex) : nullptr; }

		int m_nBoneFlexDriverCount;
		int m_nBoneFlexDriverIndex;

		// for static props (and maybe others)
		// Precomputed Per-Triangle AABB data
		int m_nPerTriAABBIndex;
		int m_nPerTriAABBNodeCount;
		int m_nPerTriAABBLeafCount;
		int m_nPerTriAABBVertCount;

		// always "" or "Titan"
		int unkstringindex;

		// ANIs are no longer used and this is reflected in many structs
		// Start of interal file data
		int vtxindex; // VTX
		int vvdindex; // VVD / IDSV
		int vvcindex; // VVC / IDCV 
		int vphyindex; // VPHY / IVPS

		int vtxsize; // VTX
		int vvdsize; // VVD / IDSV
		int vvcsize; // VVC / IDCV 
		int vphysize; // VPHY / IVPS

		inline vtx::FileHeader_t* pVTX() const { return vtxsize > 0 ? reinterpret_cast<vtx::FileHeader_t*>((char*)this + vtxindex) : nullptr; }
		inline vvd::vertexFileHeader_t* pVVD() const { return vvdsize > 0 ? reinterpret_cast<vvd::vertexFileHeader_t*>((char*)this + vvdindex) : nullptr; }
		inline vvc::vertexColorFileHeader_t* pVVC() const { return vvcsize > 0 ? reinterpret_cast<vvc::vertexColorFileHeader_t*>((char*)this + vvcindex) : nullptr; }

		// this data block is related to the vphy, if it's not present the data will not be written
		// definitely related to phy, apex phy has this merged into it
		int unkmemberindex1; // section between vphy and vtx.?
		int numunkmember1; // only seems to be used when phy has one solid

		// only seen on '_animated' suffixed models so far
		// probably added during titanfall 2's life cycle, later models have idx always, early/r2tt models do not
		int unkcount3;
		int unkindex3;

		int unused1[60];

	};
}


//ASSERT_SIZE(mstudiobone_t, 0xB4);
//ASSERT_SIZE(RMdlMeshStreamHeader, 0x24);
//ASSERT_SIZE(vertexFileHeader_t, 0x40);
//ASSERT_SIZE(RMdlFixup, 0xC);
//ASSERT_SIZE(RMdlVertex, 0x30);
//ASSERT_SIZE(mstudiobodyparts_short_t, 0x8);
//ASSERT_SIZE(RMdlModel, 0x8);
//ASSERT_SIZE(RMdlLod, 0xC);
//ASSERT_SIZE(RMdlSubmesh, 0x9);
//ASSERT_SIZE(RMdlStripGroup, 0x19);
//ASSERT_SIZE(RMdlStripVert, 0x9);
//ASSERT_SIZE(RMdlStrip, 0x1B);
//ASSERT_SIZE(RMdlExtendedWeight, 0x4);
//ASSERT_SIZE(RMdlTexture, 0xC);